fix(plugin): anchor WorktreeCreate at the project dir#3453
Conversation
…worktrees on HEAD Two failure modes observed driving Claude Code agent isolation through the plugin's WorktreeCreate hook: 1. The hook inherits the session shell's working directory. When the shell has cd'd outside the repository (e.g. into a reference-papers directory), wt switch dies with 'not a git repository' and every isolated-agent spawn in the session fails. Anchoring at CLAUDE_PROJECT_DIR (falling back to . when unset) pins the hook to the repo the session was launched in. 2. Without --base, agent worktrees are created from the default branch, so a sub-agent spawned mid-session on a feature branch is born at main and cannot see the session's committed work; its output then needs manual base correction before integration. --base=@ bases the worktree on the session's current HEAD, matching the isolated-copy-of-current-work expectation of sub-agent isolation. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
worktrunk-bot
left a comment
There was a problem hiding this comment.
Both changes check out. --base=@ is a documented shortcut (it appears in wt switch's own --long-help: wt switch --create fix --base=@ # Branch from current HEAD), and CLAUDE_PROJECT_DIR is a real Claude Code hook environment variable, so the cd "${CLAUDE_PROJECT_DIR:-.}" anchor is sound — and the :-. fallback preserves today's behavior when it's unset. Ordering is right too: the cd runs before wt switch, so @ resolves against the session's project repo rather than wherever the shell wandered. Creation-only, so no data-safety concern in the change itself.
One thing I can't approve on my own: this file (plugins/worktrunk/hooks/hooks.json) is on worktrunk's data-loss surface — it also ships the WorktreeRemove hook that runs wt remove — so per the repo's review policy any edit here goes to a human rather than a bot approval. Routing to @max-sixty for the merge call. The diff doesn't touch the removal path, so this is a policy hold, not a concern about the change.
Minor, non-blocking: this is a user-visible behavior change (agent worktrees now base on the session HEAD instead of the default branch). Worth a CHANGELOG entry, and maybe a line in skills/worktrunk/reference/claude-code.md where the WorktreeCreate/WorktreeRemove routing is described — though @max-sixty may prefer to author the changelog prose himself.
just as a process point, this is overly conservative, @worktrunk-bot you should be deciding based on whether the code actually touches code that is in data-destruction paths, not whether there are unrelated changes in the file; please fix the guidance here |
|
thanks @silvanshade Have you run with this at all yourself? I can test it myself for a while. I think your points all make sense; particularly (1); but I'm nervous about changes to this area of code given they're difficult to test. @worktrunk-bot could you compare the default behavior of Claude Code to these? |
|
Compared both changes against Claude Code's native worktree behavior (docs 1. 2.
The catch the PR body already flags — the hook can't read Two things that make me lean toward the change being reasonable despite that, but worth your call:
One smaller pre-existing gap surfaced along the way: native Sources
On the "have you run it" question — that's for @silvanshade; I can't drive a live Claude Code session from CI to observe the hook end-to-end, so I've verified the semantics from source/docs rather than by running it. |
Yes, I have been running with this configuration since Fable identified it as a fix to workflow problems we were having. The scenario described is real, I have a few separate repos which are topically related but not formally connected via submodules or anything like that, and then additional directories containing relevant research artifacts. Prior to the change, worker subagents would sometimes get tripped up during tasks that would involve navigating around to do some analysis and then interacting with worktrunk. Since the change, reports about those issues from the coordinator agents have stopped. In that regard it seems to have addressed the problem. Whether it's actually the right solution here, I don't know. |
|
Agreed — the hold should key on whether the diff actually touches a data-destruction path, not on unrelated contents of the same file. Fixed the guidance in #3457. That PR rewrites the data-loss review rule so the file-level trigger only fires when the change can reach a destructive path: source-code edits still hold on proximity (a change near the force-delete path in This PR (#3453) is still yours to merge on the substance — I flagged the |
how is this mode selected? |
|
happy to merge the |
|
It's a settings key, not a flag or a per-run choice — {
"worktree": {
"baseRef": "head"
}
}Default ( Two things worth noting for this PR:
|
|
One mechanical note on "merge the To get the |
|
@worktrunk-bot can you remove the |
Do plugins have access to this config? |
Keep the CLAUDE_PROJECT_DIR anchor but remove --base=@ so the hook no longer hardcodes head-based worktrees. Base selection stays as wt's default (the default branch), matching Claude Code's native 'fresh' default, rather than overriding the worktree.baseRef setting for everyone.
|
Done — pushed a20d1c0 dropping Do plugins have access to this config? No — not through any sanctioned channel. The
The only way the hook could reach the value is to read Sources
|
|
OK, @silvanshade — my sense is we should stay with the default, at least until CC allows for reading its config — how do you feel about that? We could also quite easily add a config for default args for the hook — lmk and I'll implement? |
The WorktreeRemove hook inherits the working directory it is fired from.
When that is not inside the repo (e.g. the session's worktree was already
removed, or the hook fires from a neutral cwd), `wt remove <path>` dies with
`fatal: not a git repository` — repo discovery starts from cwd, not the path
argument. Claude Code surfaces this as `WorktreeRemove hook failed` and keeps
the session row, so the background session becomes undeletable.
`cd "${CLAUDE_PROJECT_DIR:-.}"` pins the hook to the launch repo, mirroring
the WorktreeCreate anchor added in max-sixty#3453.
Anchor the
WorktreeCreatehook atCLAUDE_PROJECT_DIR. The hook command inherits the session shell's working directory. When the shell hascd'd outside the repository (e.g. into a reference-papers directory elsewhere on disk),wt switchdies with not a git repository and every isolated-agent spawn in the session fails — observed as a six-agent spawn failure mid-session.cd "${CLAUDE_PROJECT_DIR:-.}"pins the hook to the repository the session was launched in, falling back to the current behavior when the variable is unset.Originally this PR also added
--base=@to base agent worktrees on the session's HEAD. That change was dropped at the maintainer's request (worktrunk-bot pushed the removal) so it can be evaluated separately — hardcoding--base=@would override the nativeworktree.baseRefsetting for everyone, and the hook has no way to read that setting. This PR is now thecdanchor only.🤖 Generated with Claude Code